<?php

/*
 * 商户
 */
namespace JingjianService;

class JingjianService
{
    protected $mch_id;
    protected $mch_key;
    protected $mch_private_key;
    protected $serial_no;
    protected $wechat_cert;
    protected $error;
    function __construct()
    {
        $this->mch_id = config('home.merchant_id');
        $this->mch_key = config('home.mch_key');
        $this->mch_private_key = $this->getPrivateKey('./pem/apiclient_key.pem');
        $this->serial_no = config('home.serial_no');
        $this->wechat_cert = './pem/apiclient_cert.pem';
        $this->APIv3 = config('home.APIv3');
    }
    /**
     * 进件
     */
    public function submit($params)
    {
        if (empty($params['subject_info']->certificate_info->cert_type)) {
            unset($params['subject_info']->certificate_info);
        }
        if (empty($params['bank_account_info']->bank_account_type)) {
            unset($params['bank_account_info']);
        }
        if (empty($params['subject_info']->organization_info->organization_code)) {
            unset($params['subject_info']->organization_info);
        }
        if (empty($params['subject_info']->identity_info->id_doc_info->id_doc_name)) {
            unset($params['subject_info']->identity_info->id_doc_info);
        }
        if(!$zhengshu = $this->getzhengshu()){
            return false;
        }

        $params['contact_info']->contact_name = $this->getEncryptS($params['contact_info']->contact_name);
        $params['contact_info']->contact_id_number = $this->getEncryptS($params['contact_info']->contact_id_number);
        $params['contact_info']->mobile_phone = $this->getEncryptS($params['contact_info']->mobile_phone);
        $params['contact_info']->contact_email = $this->getEncryptS($params['contact_info']->contact_email);

        $params['subject_info']->identity_info->id_card_info->id_card_name = $this->getEncryptS($params['subject_info']->identity_info->id_card_info->id_card_name);
        $params['subject_info']->identity_info->id_card_info->id_card_number = $this->getEncryptS($params['subject_info']->identity_info->id_card_info->id_card_number);
        $params['subject_info']->ubo_info->name = $this->getEncryptS($params['subject_info']->ubo_info->name);
        $params['subject_info']->ubo_info->id_number = $this->getEncryptS($params['subject_info']->ubo_info->id_number);
        $params['bank_account_info']->account_number = $this->getEncryptS($params['bank_account_info']->account_number);
        $params['bank_account_info']->account_name = $this->getEncryptS($params['bank_account_info']->account_name);
        if (empty($params['subject_info']->identity_info->id_card_info->id_card_name)){
            $this->error = '请填写主体资料->经营者/法人身份证件->身份证信息->身份证姓名';
            return false;
        }
        if (empty($params['subject_info']->identity_info->id_card_info->id_card_number)){
            $this->error = '请填写主体资料->经营者/法人身份证件->身份证信息->身份证号码';
            return false;
        }
        if (empty($params['subject_info']->identity_info->owner == 'true')){
            $params['subject_info']->identity_info->owner = true;
        } else {
            $params['subject_info']->identity_info->owner = false;
        }
        $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/';
        $timestamp = time();
        $nonce = $this->nonce_str();

        $sign = $this->sign($url, 'POST', $timestamp, $nonce, json_encode($params), $this->mch_private_key, $this->mch_id, $this->serial_no);
        //设置header头
        $header = [
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign,
            'Accept:application/json',
            'User-Agent:' . $this->mch_id,
            'Content-Type:application/json',
            'Wechatpay-Serial:' . $zhengshu
        ];

        $result = $this->curl($url, json_encode($params), $header);
        $result = json_decode($result, true);

        if (isset($result['code']) && $result['code'] == 'PARAM_ERROR') {
            $this->error = $result['message'] . $result['detail']['field'];
            return array('code'=>2,'msg'=>$this->error);
        }
        return array('code'=>1,'msg'=>$result['applyment_id']);
    }

    /**
     * 查询进件
     */
    public function query($no)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/' . $no;
        $timestamp = time();
        $nonce = $this->nonce_str();
        $sign = $this->sign($url, 'GET', $timestamp, $nonce, '', $this->mch_private_key, $this->mch_id, $this->serial_no);
        $header = [
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign,
            'Accept:application/json',
            'User-Agent:' . $this->mch_id,
            'Content-Type:application/json',
            'Wechatpay-Serial:' . $this->getzhengshu()
        ];
        $result = $this->curl($url, '', $header, 'GET');
        $result = json_decode($result, true);
        if(!empty($result['code'])&&$result['code']=='SYSTEM_ERROR'){
            $this->error = $result['message'].'查询进件失败';
            return false;
        }
        if($result['applyment_state']=='APPLYMENT_STATE_REJECTED'){
            $this->error = $result['applyment_state_msg'];
            return $result;
        }
        return $result;
    }
    /**
     * 获取证书
     */
    public function getzhengshu()
    {
        $url = 'https://api.mch.weixin.qq.com/v3/certificates';
        $timestamp = time();
        $nonce = $this->nonce_str();
        $body = '';
        $sign = $this->sign($url, 'GET', $timestamp, $nonce, $body, $this->mch_private_key, $this->mch_id, $this->serial_no);
        //设置header头
        $header = [
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign,
            'Accept:application/json',
            'User-Agent:' . $this->mch_id
        ];

        $result = $this->curl($url, '', $header, 'GET');

        $result = json_decode($result, true);
        if(!empty($result['code'])&&$result['code']=='SYSTEM_ERROR'){
            $this->error = $result['message'].'请检查商户号证书等是否填写错误';
            return false;
        }

        $serial_no = $result['data'][0]['serial_no'];
        $encrypt_certificate = $result['data'][0]['encrypt_certificate'];

        $result = $this->decryptToString($encrypt_certificate['associated_data'], $encrypt_certificate['nonce'], $encrypt_certificate['ciphertext'], $this->APIv3);

        if(!is_dir(pathinfo($this->wechat_cert, PATHINFO_DIRNAME))){
            mkdir(pathinfo($this->wechat_cert, PATHINFO_DIRNAME));
        }

//        file_put_contents($this->wechat_cert, $result);
        return $serial_no;
    }

    private function getEncryptS($str)
    {
        //$str是待加密字符串
        $public_key = file_get_contents($this->wechat_cert);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)){
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new \Exception('encrypt failed');
        }
        return $sign;
    }
    public function getError()
    {
        return $this->error;
    }
    /**
     * 生成一个随机字符串
     */
    private function nonce_str()
    {
        return date('YmdHis', time()) . rand(10000, 99999);
    }
    /**
     *获取商户私钥
     * $path 商户私钥路径（比如：'./cert/mch_private_key.pem'）
     */
    //获取私钥
    public static function getPrivateKey($filepath) {
        return openssl_get_privatekey(file_get_contents($filepath));
    }
    /**
     * 生成签名
     */
    //签名
    private function sign($url,$http_method,$timestamp,$nonce,$body,$mch_private_key,$merchant_id,$serial_no)
    {
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message =
            $http_method."\n".
            $canonical_url."\n".
            $timestamp."\n".
            $nonce."\n".
            $body."\n";
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        $schema = 'WECHATPAY2-SHA256-RSA2048 ';
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);
        return $token;
    }
    protected function curl($url, $data = [], $header, $method = 'POST')
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        if ($method == 'POST') {
            curl_setopt($curl, CURLOPT_POST, TRUE);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        $result = curl_exec($curl);
        return $result;
    }
    /**
     * 加密字符串
     */
    public function decryptToString($associatedData,$nonceStr,$ciphertext,$aesKey)
    {
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= 16) {
            return false;
        }

        // ext-sodium (default installed on >= PHP 7.2)
        if(function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available())
        {
            return \sodium_crypto_aead_aes256gcm_is_available($ciphertext,$associatedData,$nonceStr,$aesKey);
        }
        // ext-libsodium (need install libsodium-php 1.x via pecl)
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        }

        // openssl (PHP >= 7.1 support AEAD)
        if(PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm',\openssl_get_cipher_methods())){
            $ctext = substr($ciphertext,0,-16);
            $authTag = substr($ciphertext,-16);
            return \openssl_decrypt(
                $ctext,
                'aes-256-gcm',
                $aesKey,
                \OPENSSL_RAW_DATA,
                $nonceStr,
                $authTag,
                $associatedData
            );

            throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
        }
    }
}